package dslProgramGen;

import methodCallTreeGen.MethodNode;
import methodCallTreeGen.Node;
import methodCallTreeGen.NodeType;

import java.util.*;

import static dslProgramGen.util.ClearRedundantMethodsUtil.*;
import static methodCallTreeGen.MethodCallTreeGen.constructSameMethodNameMap;

/**
 * 用于函数精简，解决相同内容函数多次定义带来的冗余问题，采用的是函数调用树相同和函数内容相同的方式
 */
@Deprecated
public class ClearRedundantMethodsByMethodCallChain {

    /**
     * 将函数名相同的函数按 函数调用链 分类
     *
     * @param sameMethodNameMethodList      函数名相同的函数列表
     * @param methodCallChain2MethodListMap 函数调用链名2函数列表，这里函数列表里的函数都有相同的函数调用链
     */
    public static void classifySameMethodNameMethodsByMethodCallChain(List<MethodNode> sameMethodNameMethodList, Map<String, List<MethodNode>> methodCallChain2MethodListMap) {
        // 遍历sameMethodNameMethodList，将每个函数按函数调用链分类
        for (MethodNode methodNodeTemp : sameMethodNameMethodList) {
            if (methodCallChain2MethodListMap.get(methodNodeTemp.methodCallChainStringBuilder.toString()) == null) {
                // 如果methodCallChain2MethodListMap没有该函数的函数调用链名称
                // 将函数调用链相同的methodNode保存在一起
                List<MethodNode> sameMethodCallChainMethodList = new ArrayList<>();
                sameMethodCallChainMethodList.add(methodNodeTemp);
                methodCallChain2MethodListMap.put(methodNodeTemp.methodCallChainStringBuilder.toString(), sameMethodCallChainMethodList);
            } else {
                // 如果methodCallChain2MethodListMap有该函数的函数调用链名称
                List<MethodNode> sameMethodCallChainMethodList = methodCallChain2MethodListMap.get(methodNodeTemp.methodCallChainStringBuilder.toString());
                sameMethodCallChainMethodList.add(methodNodeTemp);
            }
        }
    }

    /**
     * 将相同函数调用链的函数按 内容是否相同 分类 （内容相同包括该函数参数，返回值，函数体内容相同，同时包括直接和间接调用的函数的函数的参数，返回值，函数体相同）
     *
     * @param sameMethodCallChainMethodList 相同函数调用链的函数列表
     * @param sameContentMethodListList     按内容分类后的函数列表的列表
     */
    public static void classifySameMethodCallChainMethodsByMethodContens(List<MethodNode> sameMethodCallChainMethodList, List<List<MethodNode>> sameContentMethodListList) {
        // 遍历sameMethodCallChainMethodList的每一个methodNode，将其按DSL内容分类
        for (MethodNode methodNodeTemp : sameMethodCallChainMethodList) {
            if (sameContentMethodListList.size() == 0) {
                // 第一个methodNode
                List<MethodNode> sameContentMethodList = new ArrayList<>();
                sameContentMethodList.add(methodNodeTemp);
                sameContentMethodListList.add(sameContentMethodList);
            } else {
                // 表示sameContentMethodListList里面有内容,看看sameContentMethodListList里面有没有与methodNodeTemp内容相同的
                // 若有，则将该methodNode直接加入到对应的sameContentMethodList，否则，创建一个新的sameContentMethodList
                boolean hasSameMethodInSameContentMethodListList = false;
                for (int i = 0; i < sameContentMethodListList.size(); i++) {
                    MethodNode firstMethodNodeOfSameContentMethodList = sameContentMethodListList.get(i).get(0);
                    if (isTwoMethodNodeEqual(firstMethodNodeOfSameContentMethodList, methodNodeTemp)) {
                        // 若在sameContentMethodListList有与methodNodeTemp内容一样的
                        sameContentMethodListList.get(i).add(methodNodeTemp);
                        hasSameMethodInSameContentMethodListList = true;
                        break;
                    }
                }
                // 如果在sameContentMethodListList里没有与methodNodeTemp一样的
                if (!hasSameMethodInSameContentMethodListList) {
                    // 单独为该methodNodeTemp开一个list
                    List<MethodNode> sameContentMethodList = new ArrayList<>();
                    sameContentMethodList.add(methodNodeTemp);
                    sameContentMethodListList.add(sameContentMethodList);
                }
            }
        }
    }

    /**
     * 将内容相同的函数用第一个函数表示
     *
     * @param sameContentMethodListList 内容相同的函数组成的函数列表 列表，每个元素表示内容相同的函数列表
     */
    public static void representSameContentsMethodsUsingTheFirstMethod(List<List<MethodNode>> sameContentMethodListList) {
        for (List<MethodNode> sameContentMethodList : sameContentMethodListList) {
            // 获得sameContentMethodList第一个元素作为其他相同内容函数的定义
            MethodNode firstMethodNodeOfSameContentMethodList = sameContentMethodList.get(0);
            for (int i = 1; i < sameContentMethodList.size(); i++) {
                // 获得sameContentMethodList第i个methodNode
                MethodNode ithMethodNodeOfSameContentMethodList = sameContentMethodList.get(i);
                // 看看ithMethodNodeOfSameContentMethodList有没有parent节点
                if (ithMethodNodeOfSameContentMethodList.parentNode != null) {
                    // 获得ithMethodNodeOfSameContentMethodList的parentNode
                    MethodNode parentNodeOfIthMethodNode = (MethodNode) (ithMethodNodeOfSameContentMethodList.parentNode);
                    // 获得ithMethodNodeOfSameContentMethodList在parentNodeOfIthMethodNode的index
                    int ithMethodNodeIndexOfParentNodeContentList = parentNodeOfIthMethodNode.contentNodeList.indexOf(ithMethodNodeOfSameContentMethodList);
                    // 替换ithMethodNodeOfSameContentMethodList为firstMethodNodeOfSameContentMethodList
                    parentNodeOfIthMethodNode.contentNodeList.set(ithMethodNodeIndexOfParentNodeContentList, firstMethodNodeOfSameContentMethodList);
                    // parentNodeOfIthMethodNode中函数体会涉及对ithMethodNode调用语句，需要修改为firstMethodNodeOfSameContentMethodList的函数名
                    renameCallMethodNameInMethodContentList(parentNodeOfIthMethodNode.dslExpressedMethod, ithMethodNodeOfSameContentMethodList.newMethodName, firstMethodNodeOfSameContentMethodList.newMethodName);
                }
            }
        }
    }

    /**
     * 函数精简，之前可能出现完全相同的两个函数被定义了两次（甚至更多次数），现在想 内容完全相同的函数只定义一次
     *
     * @param rootNode 函数调用树的根节点
     */
    public static void clearRedundantMethodDefine(MethodNode rootNode) {
        // 函数精简，对相同的函数只定义一次（相同包括函数内容及其调用的函数的内容都相同）
        // 遍历函数调用树，统计相同函数名（旧函数名）的函数
        Map<String, List<MethodNode>> sameMethodNameMap = new HashMap<>(); // <methodName, List<MethodNode>>
        constructSameMethodNameMap(rootNode, sameMethodNameMap);
        // 统计每个函数的函数调用链（自己->[B, C, D]->[E] [F] [G,H]）
        for (String methodName : sameMethodNameMap.keySet()) {
            for (MethodNode methodNode : sameMethodNameMap.get(methodName)) {
                genMethodCallChainForAMethod(methodNode);
            }
        }
        // 对相同函数名的函数按调用函数链进行分类
        for (String methodName : sameMethodNameMap.keySet()) {
            // sameMethodNameMethodList表示函数名相同的函数列表
            List<MethodNode> sameMethodNameMethodList = sameMethodNameMap.get(methodName);
            // 将同名函数按 函数调用链名称 分类， key：函数调用链名称，value：相同函数调用链的函数列表
            Map<String, List<MethodNode>> methodCallChain2MethodListMap = new HashMap<>();
            // 将函数名相同的函数按 函数调用链 分类
            classifySameMethodNameMethodsByMethodCallChain(sameMethodNameMethodList, methodCallChain2MethodListMap);

            // 现在methodCallChain2MethodListMap的每个value即List<MethodNode> sameMethodCallChainMethodList表示他们的函数调用链一样
            // 接下来判断sameMethodCallChainMethodList里的每个函数里的内容是否完全一样，内容看DslExpressedMethod里的内容，包括参数，返回值和函数体里的内容
            for (List<MethodNode> sameMethodCallChainMethodList : methodCallChain2MethodListMap.values()) {
                // 对sameMethodCallChainMethodList里的methodNode按照内容是否相同进行分类
                List<List<MethodNode>> sameContentMethodListList = new ArrayList<>();
                // 将相同函数调用链的函数按 内容是否相同 分类 （内容相同包括该函数参数，返回值，函数体内容相同，同时包括直接和间接调用的函数的函数的参数，返回值，函数体相同）
                classifySameMethodCallChainMethodsByMethodContens(sameMethodCallChainMethodList, sameContentMethodListList);

                // 上面将内容相同的methodNode分类到一起，由List<List<MethodNode>> sameContentMethodListList元素表示
                // List<MethodNode> sameContentMethodList为相同内容的methodNode的集合，我们选择集合中的第一个元素作为相同函数的唯一定义
                representSameContentsMethodsUsingTheFirstMethod(sameContentMethodListList);
            }
        }
    }



    /**
     * 判断两个methodNode是否一样，主要看他们的DslExpressedMethod是否一样
     * DslExpressedMethod参数是否一样，返回类型是否一样，函数体是否一样
     * 调用的函数的函数体是否一样
     *
     * @param methodNode1
     * @param methodNode2
     * @return
     */
    public static boolean isTwoMethodNodeEqual(MethodNode methodNode1, MethodNode methodNode2) {
        DslExpressedMethod dslExpressedMethod1 = methodNode1.dslExpressedMethod;
        DslExpressedMethod dslExpressedMethod2 = methodNode2.dslExpressedMethod;
        // 判断两个dslExpressedMethod的参数是否一样
        boolean isParametersEquals = isTwoListEqual(dslExpressedMethod1.parameters, dslExpressedMethod2.parameters);
        if (!isParametersEquals) {
            // 如果参数不同
            return false;
        }
        // 判断两个dslExpressedMethod的返回类型是否一样
        boolean isReturnTypeEquals = dslExpressedMethod1.returnType.equals(dslExpressedMethod2.returnType);
        if (!isReturnTypeEquals) {
            // 如果返回类型不同
            return false;
        }
        // 判断两个dslExpressedMethod的函数体是否一样
        boolean isMethodContentListEquals = isTwoDslMethodContentListEquals(dslExpressedMethod1.methodContentList, dslExpressedMethod2.methodContentList);
        if (!isMethodContentListEquals) {
            // 如果函数体不同
            return false;
        }
        // 判断函数调用的子函数个数是否一样
        if (getSubMethodNum(methodNode1) != getSubMethodNum(methodNode2)) {
            // 若两个函数调用子函数个数不同
            return false;
        }
        // 判断每个调用的子函数是否一样
        for (int i = 0; i < getSubMethodNum(methodNode1); i++) {
            MethodNode ithSubMethodNode1 = getNthSubMethodNode(methodNode1, i);
            MethodNode ithSubMethodNode2 = getNthSubMethodNode(methodNode2, i);
            if (!isTwoMethodNodeEqual(ithSubMethodNode1, ithSubMethodNode2)) {
                // 如果两个函数的第i个子函数不相等
                return false;
            }
        }
        return true;
    }

    /**
     * 判断两个Dsl表示的函数体是否相同
     *
     * @param methodContentList1
     * @param methodContentList2
     * @return
     */
    public static boolean isTwoDslMethodContentListEquals(List<String> methodContentList1, List<String> methodContentList2) {
        if (methodContentList1.size() != methodContentList2.size()) {
            // 如果两个list的size大小不同，返回false
            return false;
        } else {
            // 看两个list的每个元素是否相同
            for (int i = 0; i < methodContentList1.size(); i++) {
                // 表示methodContentList1第i个元素内容
                String ithDslStatementOfMethodContentList1 = methodContentList1.get(i);
                if (isMethodCallDslStatement(ithDslStatementOfMethodContentList1)) {
                    // 若methodContentList1第i个元素内容为函数调用
                    // 重写调用语句去除_vi( 中的i
                    // 例如methodName_v0(school_0, student_0) 去除后为methodName_v(school_0, student_0)
                    ithDslStatementOfMethodContentList1 = ithDslStatementOfMethodContentList1.replaceAll("_v\\d+\\(", "_v(");
                    String ithDslStatementOfMethodContentList2 = methodContentList2.get(i);
                    ithDslStatementOfMethodContentList2 = ithDslStatementOfMethodContentList2.replaceAll("_v\\d+\\(", "_v(");
                    if (!ithDslStatementOfMethodContentList1.equals(ithDslStatementOfMethodContentList2)) {
                        // 若两个list的第i个元素即函数调用不一样
                        return false;
                    }
                } else {
                    // 若methodContentList1第i个元素内容不为函数调用
                    if (!ithDslStatementOfMethodContentList1.equals(methodContentList2.get(i))) {
                        // 若两个list的第i个元素内容不同
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * 对一个MethodNode产生它的函数调用链，例如该函数是A，调用了B,C,D函数，B函数调用了E函数，C函数调用了F函数，D函数调用了G,H函数(E,F,G,H函数不再调用其他函数)
     * 则其函数函数调用链为 A , -> [B , C , D , ] -> [E , ] [F , ] [G , H , ] -> [] [] [] []
     *
     * @param methodNode 处理的MethodNode
     */
    public static void genMethodCallChainForAMethod(MethodNode methodNode) {
        StringBuilder methodCallChainStringBuilder = methodNode.methodCallChainStringBuilder;
        Queue<MethodNode> methodNodeQueue = new LinkedList<>();
        methodNodeQueue.add(methodNode);
        while (!methodNodeQueue.isEmpty()) {
            int methodNodeQueueSize = methodNodeQueue.size();
            StringBuilder stringBuilderTemp = new StringBuilder();
            for (int i = 0; i < methodNodeQueueSize; i++) {
                MethodNode pollMethodNode = methodNodeQueue.poll();
                if ("leftOpenBracket".equals(pollMethodNode.methodName)) {
                    stringBuilderTemp.append("[");
                } else if ("rightOpenBracket".equals(pollMethodNode.methodName)) {
                    stringBuilderTemp.append("] ");
                } else {
                    stringBuilderTemp.append(pollMethodNode.methodName + " , ");
//                    // 统计该pollMethodNode是否还调用了其他methodNode
//                    boolean isCallOtherMethod = false;
//                    for (Node node : pollMethodNode.contentNodeList) {
//                        if (node.nodeType == NodeType.METHOD) {
//                            isCallOtherMethod = true;
//                            break;
//                        }
//                    }
                    // 若该pollMethodNode调用了其他method
//                    if (isCallOtherMethod) {
                    methodNodeQueue.add(new MethodNode(null, "leftOpenBracket", null));
                    // 将pollMethodNode的每个子函数加入队列
                    for (Node node : pollMethodNode.contentNodeList) {
                        if (node.nodeType == NodeType.METHOD) {
                            methodNodeQueue.add((MethodNode) node);
                        }
                    }
                    methodNodeQueue.add(new MethodNode(null, "rightOpenBracket", null));
//                    }
                }
            }
            stringBuilderTemp.append(" -> ");
            methodCallChainStringBuilder.append(stringBuilderTemp);
        }
    }

}
